/**************************************************************************
File:        hal.h
Description:

Copyright:   (C) 2012 by ZJ Laczik
Email:       Zsolt.Laczik@eng.ox.ac.uk
****************************************************************************
Change log:
   begin      Saturday 18 February 2012
***************************************************************************/

#include <stdlib.h>
#include <stdio.h>

// #include <unistd.h>
// #include <sys/fcntl.h>
// #include <sys/ioctl.h>
// #include <iostream>
// #include <fstream>

#include "../../kernel_drivers/pp/pp.h"

#include "hal.h"

#define USE_DUMMY_FOR_DIGITAL_IO	0
#define USE_IP28_FOR_DIGITAL_IO		1
#define USE_USBDUX_FOR_DIGITAL_IO	2
#define USE_ZJLPP_FOR_DIGITAL_IO	3

Hal::Hal(QObject *parent) : QObject(parent) {
    init();
    mDigitalIOtype = USE_DUMMY_FOR_DIGITAL_IO;
}

Hal::~Hal() {
    if (mUSBDUX)
        comedi_close(mUSBDUX);

    if (ppdev)
	close(ppdev);
}

bool Hal::init()
{
    // Check what hardware is available
    mIP28IsReady = false;
    mZJLPPIsReady = mfInitZJLPP();
    mUSBDUXIsReady = mfInitUSBDUX();

  return (mIP28IsReady || mZJLPPIsReady || mUSBDUXIsReady);
}

void Hal::slotSelectDummyCtrl()
{
    mDigitalIOtype = USE_DUMMY_FOR_DIGITAL_IO;
    emit(signalDspMsg(QString("Dummy IO selected"), false));
}

void Hal::slotSelectIP28Ctrl()
{
    mDigitalIOtype = USE_IP28_FOR_DIGITAL_IO;
    emit(signalDspMsg(QString("IP28 IO selected"), false));
}

void Hal::slotSelectComediUSBDUXCtrl()
{
    mDigitalIOtype = USE_USBDUX_FOR_DIGITAL_IO;
    emit(signalDspMsg(QString("Comedi USB-DUX IO selected"), false));
}

void Hal::slotSelectZJLPPCtrl()
{
    mDigitalIOtype = USE_ZJLPP_FOR_DIGITAL_IO;
    emit(signalDspMsg(QString("Parallel printer port IO selected"), false));
}

bool Hal::mfInitZJLPP()
{
    ppdev=open("/dev/zjl_pp",O_RDWR) ;
    if (ppdev == -1) {
//        std::cerr<<"zjl_pp: no such device"<<std::endl ;
        emit(signalDspMsg(QString("Cannot open ZJLPP device"), false));
        return false;
    }
    return true;
}

bool Hal::mfInitUSBDUX()
{
    int ch;
    int subdevice;
    int stype;

//    printf("mfInitUSBDUX(): start\n"); fflush(stdout);

    mUSBDUX = comedi_open("/dev/comedi0");
    if (!mUSBDUX)
    {
        emit(signalDspMsg(QString("Cannot open USBDUX Comedi device"), false));
        return false;
    }

//    printf("mfInitUSBDUX(): opened\n"); fflush(stdout);

    subdevice = 2;
    stype = comedi_get_subdevice_type(mUSBDUX, subdevice);
    if (stype != COMEDI_SUBD_DIO) {
        emit(signalDspMsg(QString("USBDUX subdevice #2 is not an I/I device"), false));
        return false;
    }

//    printf("mfInitUSBDUX(): subdevice checked\n"); fflush(stdout);

    for (ch=0; ch<8; ch++)
    {
        if (0 > comedi_dio_config(mUSBDUX,subdevice,ch,COMEDI_OUTPUT))
        {
            emit(signalDspMsg(QString("Cannot configure USBDUX subdevice #2 channel %1 as output") .arg(ch), false ));
            return false;
        }

//   printf("mfInitUSBDUX(): channel %d configured\n", ch); fflush(stdout);

    }
    emit(signalDspMsg(QString("Configured USBDUX subdevice #2 channels 0 to 7 as output"), false));
    return true;
}

bool Hal::mfIP28IsReady()
{
    return mIP28IsReady;
}

bool Hal::mfZJLPPIsReady()
{
    return mZJLPPIsReady;
}

bool Hal::mfUSBDUXIsReady()
{
//    printf("mfUSBDUXIsReady(): %d\n",mUSBDUXIsReady); fflush(stdout);
    return mUSBDUXIsReady;
}

bool Hal::mfReadInterlockBit()
{
    return true;

    // not yet implemented
    switch (mDigitalIOtype) {
    case USE_IP28_FOR_DIGITAL_IO:
    
      break;
    case USE_USBDUX_FOR_DIGITAL_IO:

    break;
      case USE_ZJLPP_FOR_DIGITAL_IO:

      break;
    case USE_DUMMY_FOR_DIGITAL_IO:
    default:

      break;
  }
    ////return (inb(LPT_STAT)&0x80) ;
}

bool Hal::mfReadSLMSyncBit()
{
  return true;

  // not yet implemented
  switch (mDigitalIOtype) {
    case USE_IP28_FOR_DIGITAL_IO:
    
      break;
    case USE_USBDUX_FOR_DIGITAL_IO:

    break;
      case USE_ZJLPP_FOR_DIGITAL_IO:

      break;
    case USE_DUMMY_FOR_DIGITAL_IO:
    default:

      break;
  }
}

void Hal::mfSetWarningSignBit(bool value)
{
  return;
  
  // not yet implemented
  switch (mDigitalIOtype) {
    case USE_IP28_FOR_DIGITAL_IO:
    
      break;
    case USE_USBDUX_FOR_DIGITAL_IO:

    break;
      case USE_ZJLPP_FOR_DIGITAL_IO:

      break;
    case USE_DUMMY_FOR_DIGITAL_IO:
    default:

      break;
  }
//    printf("mfSetWarningSignBit(%d) called\n",value); fflush(stdout);

    //// portvalue|=0x01; outb(portvalue,LPT_BASE); mShutterOpen=false;

    ////portvalue&=~0x01; outb(portvalue,LPT_BASE); mShutterOpen=true;


    // set warning sign line (bit0) to low
    // set trigger line (bit1) to high
////	portvalue = 0x03;
////  outb(portvalue,LPT_BASE);
}

void Hal::mfSetLaserTriggerBit(bool value)
{
  switch (mDigitalIOtype) {
    case USE_IP28_FOR_DIGITAL_IO:
	// not yet implemented    
	break;
    case USE_USBDUX_FOR_DIGITAL_IO:
	// USBDUX IO port bit 7
	if (value)
	    comedi_dio_write(mUSBDUX,2,7,1);
	else
	    comedi_dio_write(mUSBDUX,2,7,0);
	break;
    case USE_ZJLPP_FOR_DIGITAL_IO:
	if (value)
	  ioctl(ppdev,PP_SET_BIT,7);
	else
	  ioctl(ppdev,PP_CLEAR_BIT,7);
	break;
    case USE_DUMMY_FOR_DIGITAL_IO:
    default:
	// do nothing
	break;
  }
//    printf("mfSetLaserTriggerBit(%d) called\n",value); fflush(stdout);
}


void Hal::mfSetXDirBit(bool value)
{
  switch (mDigitalIOtype) {
    case USE_IP28_FOR_DIGITAL_IO:
	// not yet implemented
	break;
    case USE_USBDUX_FOR_DIGITAL_IO:
	// USBDUX IO port bit 2
	if (value) {
	    comedi_dio_write(mUSBDUX,2,6,1);	// USBDUX Comedi (device, subdevice, channel, bit_value)
	}
	else {
	    comedi_dio_write(mUSBDUX,2,6,0);
	}
	break;
    case USE_ZJLPP_FOR_DIGITAL_IO:
	if (value)
	  ioctl(ppdev,PP_SET_BIT,0);
	else
	  ioctl(ppdev,PP_CLEAR_BIT,0);
	  break;
    case USE_DUMMY_FOR_DIGITAL_IO:
    default:
	// do nothing
	break;
  }
}

void Hal::mfSetYDirBit(bool value)
{
  switch (mDigitalIOtype) {
    case USE_IP28_FOR_DIGITAL_IO:
	// not yet implemented
	break;
    case USE_USBDUX_FOR_DIGITAL_IO:
	// USBDUX IO port bit 2
	if (value) {
	    comedi_dio_write(mUSBDUX,2,4,1);	// USBDUX Comedi (device, subdevice, channel, bit_value)
	}
	else {
	    comedi_dio_write(mUSBDUX,2,4,0);
	}
	break;
    case USE_ZJLPP_FOR_DIGITAL_IO:
	if (value)
	  ioctl(ppdev,PP_SET_BIT,0);
	else
	  ioctl(ppdev,PP_CLEAR_BIT,0);
	break;
    case USE_DUMMY_FOR_DIGITAL_IO:
    default:
	// do nothing
	break;
  }
}

void Hal::mfSetZDirBit(bool value)
{
  switch (mDigitalIOtype) {
    case USE_IP28_FOR_DIGITAL_IO:
	// not yet implemented
	break;
    case USE_USBDUX_FOR_DIGITAL_IO:
	// USBDUX IO port bit 2
	if (value) {
	    comedi_dio_write(mUSBDUX,2,2,1);	// USBDUX Comedi (device, subdevice, channel, bit_value)
	}
	else {
	    comedi_dio_write(mUSBDUX,2,2,0);
	}
	break;
    case USE_ZJLPP_FOR_DIGITAL_IO:
	if (value)
	  ioctl(ppdev,PP_SET_BIT,0);
	else
	  ioctl(ppdev,PP_CLEAR_BIT,0);
	break;
    case USE_DUMMY_FOR_DIGITAL_IO:
    default:
	// do nothing
	break;
  }
}

void Hal::mfSetXStepBit(bool value)
{
  switch (mDigitalIOtype) {
    case USE_IP28_FOR_DIGITAL_IO:
	// not yet implemented
	break;
    case USE_USBDUX_FOR_DIGITAL_IO:
	// USBDUX IO port bit 5
	if (value)
	    comedi_dio_write(mUSBDUX,2,5,1);	// USBDUX Comedi (device, subdevice, channel, bit_value)
	else
	    comedi_dio_write(mUSBDUX,2,5,0);
	break;
    case USE_ZJLPP_FOR_DIGITAL_IO:
	// PP bit 1
	if (value)
	  ioctl(ppdev,PP_SET_BIT,1);
	else
	  ioctl(ppdev,PP_CLEAR_BIT,1);
	break;
    case USE_DUMMY_FOR_DIGITAL_IO:
    default:
	// do nothing
	break;
  }
}

void Hal::mfSetYStepBit(bool value)
{
  switch (mDigitalIOtype) {
    case USE_IP28_FOR_DIGITAL_IO:
	// not yet implemented
	break;
    case USE_USBDUX_FOR_DIGITAL_IO:
	// USBDUX IO port bit 3
	if (value)
	    comedi_dio_write(mUSBDUX,2,3,1);	// USBDUX Comedi (device, subdevice, channel, bit_value)
	else
	    comedi_dio_write(mUSBDUX,2,3,0);
	break;
    case USE_ZJLPP_FOR_DIGITAL_IO:
	// PP bit 2
	if (value)
	  ioctl(ppdev,PP_SET_BIT,2);
	else
	  ioctl(ppdev,PP_CLEAR_BIT,2);
	break;
    case USE_DUMMY_FOR_DIGITAL_IO:
    default:
	// do nothing
	break;
  }
}

void Hal::mfSetZStepBit(bool value)
{
  switch (mDigitalIOtype) {
    case USE_IP28_FOR_DIGITAL_IO:
	// not yet implemented
	break;
    case USE_USBDUX_FOR_DIGITAL_IO:
	// USBDUX IO port bit 1
	if (value)
	    comedi_dio_write(mUSBDUX,2,1,1);	// USBDUX Comedi (device, subdevice, channel, bit_value)
	else
	    comedi_dio_write(mUSBDUX,2,1,0);
	break;
    case USE_ZJLPP_FOR_DIGITAL_IO:
	// PP bit 3
	if (value)
	  ioctl(ppdev,PP_SET_BIT,3);
	else
	  ioctl(ppdev,PP_CLEAR_BIT,3);
	break;
    case USE_DUMMY_FOR_DIGITAL_IO:
    default:
	// do nothing
	break;
  }
}
